/**
 * Draw3D的构造函数
 */
var Draw3D = function(atEye, gl) {
  this.setEye(atEye[0], atEye[1], atEye[2]);
  this.gl = gl;

  if (!initShaders(this.gl, this.VSHADER_SOURCE, this.FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }
  this.u_ModelMatrix =
      this.gl.getUniformLocation(this.gl.program, 'u_ModelMatrix');
  this.u_MvpMatrix = this.gl.getUniformLocation(this.gl.program, 'u_MvpMatrix');
  this.u_NormalMatrix =
      this.gl.getUniformLocation(this.gl.program, 'u_NormalMatrix');
  this.u_LightColor =
      this.gl.getUniformLocation(this.gl.program, 'u_LightColor');
  this.u_LightPosition =
      this.gl.getUniformLocation(this.gl.program, 'u_LightPosition');
  this.u_AmbientLight =
      this.gl.getUniformLocation(this.gl.program, 'u_AmbientLight');
  if (!this.u_ModelMatrix || !this.u_MvpMatrix || !this.u_NormalMatrix ||
      !this.u_LightColor || !this.u_LightPosition || !this.u_AmbientLight) {
    console.log('Failed to get the storage location');
    return;
  }
};

Draw3D.prototype.VSHADER_SOURCE = 'attribute vec4 a_Position;\n' +
    'attribute vec4 a_Color;\n' +       //物体颜色
    'attribute vec4 a_Normal;\n' +      //法向量
    'uniform mat4 u_MvpMatrix;\n' +     //模型视图投影矩阵
    'uniform mat4 u_ModelMatrix;\n' +   // 模型矩阵
    'uniform mat4 u_NormalMatrix;\n' +  // 法向量变换矩阵
    'varying vec4 v_Color;\n' +
    'varying vec3 v_Normal;\n' +
    'varying vec3 v_Position;\n' +
    'void main() {\n' +
    '  gl_Position = u_MvpMatrix * a_Position;\n' +
    // 计算世界坐标下的定点坐标
    '  v_Position = vec3(u_ModelMatrix * a_Position);\n' +
    '  v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
    '  v_Color = a_Color;\n' +
    '}\n';

// Fragment shader program
Draw3D.prototype.FSHADER_SOURCE = '#ifdef GL_ES\n' +
    'precision mediump float;\n' +
    '#endif\n' +
    'uniform vec3 u_LightColor;\n' +     // 光照颜色
    'uniform vec3 u_LightPosition;\n' +  // 光的位置
    'uniform vec3 u_AmbientLight;\n' +   // 环境光的颜色
    'varying vec3 v_Normal;\n' +
    'varying vec3 v_Position;\n' +
    'varying vec4 v_Color;\n' +
    'void main() {\n' +
    // 对法线进行归一化
    '  vec3 normal = normalize(v_Normal);\n' +
    // 计算光线方向并归一化
    '  vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +
    // 计算光线方向和法向量的点积
    '  float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
    // 计算光照效果和环境光照效果
    '  vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;\n' +
    '  vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
    //计算最终光照效果
    '  gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' +
    '}\n';
//
// function draw3D(opt_src) {
//     draw3D.this.Eye = (0.0, 0.0, 14);
// };

function main() {
  var canvas = document.getElementById('draw3Dobj');

  var gl = WebGLUtils.setupWebGL(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }


  gl.clearColor(0.0, 0.0, 0.0, 0.0);
  gl.enable(gl.DEPTH_TEST);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  var draw3D0 = new Draw3D([0, 0, 14], gl);
  var draw3D1 = new Draw3D([0, 0, 14], gl);
  draw3D0.drawBall(
      [0.0, 1.0, 0.0, 1.0], [0.0, 1.0, 0.0], 0.5, 1, [1.0, 0.0, 0.0]);
  var modelMatrix = new Matrix4();
  modelMatrix.setTranslate(1, 1, 1);
  draw3D0.setModelMatrix(modelMatrix);
  draw3D0.updateBall([0.0, -1.0, 0.0, 1.0], [0.0, 1.0, 0.0]);
  draw3D0.updateBall([0.0, 1.0, 0.0, 1.0], [0.0, 1.0, 0.0]);
};

//设置视点
Draw3D.prototype.Eye = new Array(0, 0, 14);
Draw3D.prototype.setEye = function(eyeX, eyeY, eyeZ) {
  this.Eye[0] = eyeX;
  this.Eye[1] = eyeY;
  this.Eye[2] = eyeZ;
};

//外部的变换矩阵，
Draw3D.prototype.MODEL_MATRIX = new Matrix4();
// 通过此函数可以是图形变化
Draw3D.prototype.setModelMatrix = function(modelMatrix) {
  this.MODEL_MATRIX = modelMatrix;
};

/**
 * 画圆柱，
 * vertice1 上圆心
 * vertice2 下圆心
 * r 半径
 * colors 颜色
 * this.gl
 */
Draw3D.prototype.gl;
Draw3D.prototype.n;
Draw3D.prototype.u_ModelMatrix;
Draw3D.prototype.u_MvpMatrix;
Draw3D.prototype.u_NormalMatrix;
Draw3D.prototype.u_LightColor;
Draw3D.prototype.u_LightPosition;
Draw3D.prototype.u_AmbientLight;


Draw3D.prototype.drawCylinder = function(vertice1, vertice2, r, colors) {



  //圆柱的上下两点的向量
  var vector2_1 = new Array(3);
  //从原点到圆柱中心的向量 = 上下圆心的中点坐标
  var vector0_c = new Array(3);

  for (var i = 0; i < vector2_1.length; i++) {
    vector2_1[i] = vertice1[i] / vertice1[3] - vertice2[i] / vertice2[3];
    vector0_c[i] = (vertice1[i] / vertice1[3] + vertice2[i] / vertice2[3]) / 2;
  }

  var high = Math.sqrt(
      vector2_1[0] * vector2_1[0] + vector2_1[1] * vector2_1[1] +
      vector2_1[2] * vector2_1[2]);

  // 上下两个圆面的y坐标
  var y1 = high / 2;
  var y2 = (-1) * high / 2;


  // console.log(modelMatrix.elements);
  //初始化缓存区
  this.n = this.cylinderInitVertexBuffers(y1, y2, r, colors);
  if (this.n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }



  this.updateCylinder(vertice1, vertice2);
};

Draw3D.prototype.updateCylinder = function(vertice1, vertice2) {
  //圆柱的上下两点的向量
  var vector2_1 = new Array(3);
  //从原点到圆柱中心的向量 = 上下圆心的中点坐标
  var vector0_c = new Array(3);
  for (var i = 0; i < vector2_1.length; i++) {
    vector2_1[i] = vertice1[i] / vertice1[3] - vertice2[i] / vertice2[3];
    vector0_c[i] = (vertice1[i] / vertice1[3] + vertice2[i] / vertice2[3]) / 2;
  }

  //变换矩阵
  var modelMatrix = new Matrix4();
  var a_zx;
  var a_z;
  if (vector2_1[0] == 0) {
    a_zx = Math.PI / 2;
    if (vector2_1[2] < 0) {
      a_zx = a_zx * (-1);
    }
  } else {
    if (vector2_1[2] == 0) {
      if (vector2_1[0] > 0) {
        a_zx = 0;
      } else {
        a_zx = Math.PI;
      }

    } else {
      a_zx = Math.atan(vector2_1[2] / vector2_1[0]);
      if (vector2_1[0] < 0) {
        a_zx += Math.PI;
      }
    }
  }

  var x_y_z = Math.sqrt(
      vector2_1[0] * vector2_1[0] + vector2_1[1] * vector2_1[1] +
      vector2_1[2] * vector2_1[2]);
  if (vector2_1[1] == 0) {
    a_z = Math.PI / 2;
  } else {
    a_z = Math.acos(vector2_1[1] / x_y_z);
  }

  modelMatrix.setTranslate(0, 0, 0);

  // MODEL_MATRIX为外部的变换矩阵，通过此矩阵从外部传入图形的变化
  modelMatrix.multiply(this.MODEL_MATRIX);

  modelMatrix.translate(vector0_c[0], vector0_c[1], vector0_c[2]);
  modelMatrix.rotate(180 * a_zx / Math.PI, 0, 1, 0);
  modelMatrix.rotate(180 * a_z / Math.PI, 0, 0, 1);
  modelMatrix.rotate((-1) * 180 * a_zx / Math.PI, 0, 1, 0);



  // 设置光照颜色 (white)
  this.gl.uniform3f(this.u_LightColor, 1.0, 1.0, 1.0);
  // 光位置 (in the world coordinate)
  this.gl.uniform3f(this.u_LightPosition, 2.3, 4.0, 3.5);
  // 环境光
  this.gl.uniform3f(this.u_AmbientLight, 0.2, 0.2, 0.2);

  var mvpMatrix = new Matrix4();
  var normalMatrix = new Matrix4();

  mvpMatrix.setPerspective(30, 1, 1, 100);
  mvpMatrix.lookAt(this.Eye[0], this.Eye[1], this.Eye[2], 0, 0, 0, 0, 1, 0);

  mvpMatrix.multiply(modelMatrix);

  normalMatrix.setInverseOf(modelMatrix);
  normalMatrix.transpose();

  this.gl.uniformMatrix4fv(this.u_ModelMatrix, false, modelMatrix.elements);

  this.gl.uniformMatrix4fv(this.u_MvpMatrix, false, mvpMatrix.elements);

  this.gl.uniformMatrix4fv(this.u_NormalMatrix, false, normalMatrix.elements);
  // this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
  this.gl.drawElements(this.gl.TRIANGLES, this.n, this.gl.UNSIGNED_SHORT, 0);
};

Draw3D.prototype.cylinderInitVertexBuffers = function(y1, y2, r, color) {

  var num = 240;                              //一个园面的扇形数
  var vertice_num = ((num + 1) * 3 + 1) * 2;  //点数量
  //点位置
  var vertices = new Float32Array(vertice_num * 3);
  //点颜色
  var colors = new Float32Array(vertice_num * 3);
  //法向量
  var normals = new Float32Array(vertice_num * 3);
  //点索引
  var indices = new Uint16Array(num * 4 * 3);


  //计算圆上的点
  //上下两个圆面的圆心
  var index = 0;
  var tmpVertice = new Array();
  var tmpNormal1 = new Array();
  var tmpNormal2 = new Array();
  tmpVertice[0] = 0;
  tmpVertice[1] = y1;
  tmpVertice[2] = 0;
  vertices.set(tmpVertice, index);
  colors.set(color, index);
  normals.set([0, 1, 0], index);
  index += 3;

  tmpVertice[1] = y2;
  vertices.set(tmpVertice, index);
  colors.set(color, index);
  normals.set([0, -1, 0], index);
  index += 3;

  //圆上的点
  var aa = 0;
  var a = (1 / num) * Math.PI;
  tmpNormal1[1] = 0;
  tmpNormal2[1] = 0;
  for (var i = 0; i < num + 1; i++) {
    aa = (i / num) * 2 * Math.PI;
    tmpNormal1[0] = Math.cos(aa - a);
    tmpNormal1[2] = Math.sin(aa - a);
    tmpNormal2[0] = Math.cos(aa + a);
    tmpNormal2[2] = Math.sin(aa + a);

    tmpVertice[0] = r * Math.cos(aa);
    tmpVertice[1] = y1;
    tmpVertice[2] = r * Math.sin(aa);


    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set(tmpNormal1, index);
    index += 3;
    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set([0, 1, 0], index);
    index += 3;
    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set(tmpNormal2, index);
    index += 3;

    tmpVertice[1] = y2;
    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set(tmpNormal1, index);
    index += 3;
    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set([0, -1, 0], index);
    index += 3;
    vertices.set(tmpVertice, index);
    colors.set(color, index);
    normals.set(tmpNormal2, index);
    index += 3;
  }

  //计算三角形的组合
  var index = 0;
  var tIndices = new Array(3);

  for (var i = 3; i < vertice_num - 6; i += 6) {
    tIndices[0] = 0;
    tIndices[1] = i;
    tIndices[2] = i + 6;
    indices.set(tIndices, index);
    index += 3;

    tIndices[0] = i + 1;
    tIndices[1] = i + 4;
    tIndices[2] = i + 5;
    indices.set(tIndices, index);
    index += 3;

    tIndices[0] = i + 4;
    tIndices[1] = i + 5;
    tIndices[2] = i + 8;
    indices.set(tIndices, index);
    index += 3;

    tIndices[0] = 1;
    tIndices[1] = i + 3;
    tIndices[2] = i + 9;
    indices.set(tIndices, index);
    index += 3;
  }


  if (!this.initArrayBuffer('a_Position', vertices, 3)) return -1;
  if (!this.initArrayBuffer('a_Color', colors, 3)) return -1;
  if (!this.initArrayBuffer('a_Normal', normals, 3)) return -1;

  // Unbind the buffer object
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);

  // Write the indices to the buffer object
  var indexBuffer = this.gl.createBuffer();
  if (!indexBuffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  this.gl.bufferData(
      this.gl.ELEMENT_ARRAY_BUFFER, indices, this.gl.STATIC_DRAW);

  return indices.length;
};

/**
 * [this.drawBall 画球]
 * @param  {四元数组} vertice1 球心坐标
 * @param  {三元数组} vector 球的上方向
 * @param  {数值} half    为0.5时为半球,为1时为整球
 * @param  {数值} r       球半径
 * @param  {[三元数组]} colors  球颜色
 * @param  {[type]} this.gl      [description]
 */
Draw3D.prototype.drawBall = function(vertice, vector, half, r, colors) {
  //初始化缓存区
  this.n = this.ballInitVertexBuffers(half, r, colors);
  // this.n = this.cylinderInitVertexBuffers([0,1,0,1], [0,-1,0,1], 1, colors);
  if (this.n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  this.updateBall(vertice, vector);
};

Draw3D.prototype.updateBall = function(vertice, vector) {

  var modelMatrix = new Matrix4();
  var a_zx;
  var a_z;
  ;
  if (vector[0] == 0) {
    a_zx = Math.PI / 2;
    if (vector[2] < 0) {
      a_zx = a_zx * (-1);
    }
  } else {
    if (vector[2] == 0) {
      if (vector[0] > 0) {
        a_zx = 0;
      } else {
        a_zx = Math.PI;
      }

    } else {
      a_zx = Math.atan(vector[2] / vector[0]);
      if (vector[0] < 0) {
        a_zx += Math.PI;
      }
    }
  }
  var x_y_z = Math.sqrt(
      vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
  if (vector[1] == 0) {
    a_z = Math.PI / 2;
  } else {
    a_z = Math.acos(vector[1] / x_y_z);
  }

  console.log(this.u_ModelMatrix);
  modelMatrix.setTranslate(0, 0, 0);

  modelMatrix.multiply(this.MODEL_MATRIX);

  modelMatrix.translate(vertice[0], vertice[1], vertice[2]);
  modelMatrix.rotate(180 * a_zx / Math.PI, 0, 1, 0);
  modelMatrix.rotate(180 * a_z / Math.PI, 0, 0, 1);
  modelMatrix.rotate((-1) * 180 * a_zx / Math.PI, 0, 1, 0);

  // 设置光照颜色 (white)
  this.gl.uniform3f(this.u_LightColor, 1.0, 1.0, 1.0);
  // 光位置 (in the world coordinate)
  this.gl.uniform3f(this.u_LightPosition, 2.3, 4.0, 3.5);
  // 环境光
  this.gl.uniform3f(this.u_AmbientLight, 0.2, 0.2, 0.2);


  var mvpMatrix = new Matrix4();
  var normalMatrix = new Matrix4();

  mvpMatrix.setPerspective(30, 1, 1, 100);
  mvpMatrix.lookAt(this.Eye[0], this.Eye[1], this.Eye[2], 0, 0, 0, 0, 1, 0);

  mvpMatrix.multiply(modelMatrix);

  normalMatrix.setInverseOf(modelMatrix);
  normalMatrix.transpose();

  this.gl.uniformMatrix4fv(this.u_ModelMatrix, false, modelMatrix.elements);

  this.gl.uniformMatrix4fv(this.u_MvpMatrix, false, mvpMatrix.elements);

  this.gl.uniformMatrix4fv(this.u_NormalMatrix, false, normalMatrix.elements);
  // this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
  this.gl.drawElements(this.gl.TRIANGLES, this.n, this.gl.UNSIGNED_SHORT, 0);
};
Draw3D.prototype.ballInitVertexBuffers = function(half, r, color) {
  var SPHERE_DIV = 26;
  var colors = new Float32Array((SPHERE_DIV + 1) * (SPHERE_DIV + 1) * 3);
  var i, ai, si, ci;
  var j, aj, sj, cj;
  var p1, p2;

  var positions = [];
  var indices = [];


  var index = 0;
  // Generate coordinates
  for (j = 0; j <= SPHERE_DIV; j++) {
    aj = j * Math.PI * half / SPHERE_DIV;
    sj = r * Math.sin(aj);
    cj = r * Math.cos(aj);
    for (i = 0; i <= SPHERE_DIV; i++) {
      ai = i * 2 * Math.PI / SPHERE_DIV;
      si = Math.sin(ai);
      ci = Math.cos(ai);

      positions.push(si * sj);  // X
      positions.push(cj);       // Y
      positions.push(ci * sj);  // Z

      colors.set(color, index);
      index += 3;
    }
  }

  // Generate indices
  for (j = 0; j < SPHERE_DIV; j++) {
    for (i = 0; i < SPHERE_DIV; i++) {
      p1 = j * (SPHERE_DIV + 1) + i;
      p2 = p1 + (SPHERE_DIV + 1);

      indices.push(p1);
      indices.push(p2);
      indices.push(p1 + 1);

      indices.push(p1 + 1);
      indices.push(p2);
      indices.push(p2 + 1);
    }
  }


  if (!this.initArrayBuffer('a_Position', new Float32Array(positions), 3))
    return -1;
  if (!this.initArrayBuffer('a_Color', colors, 3)) return -1;
  if (!this.initArrayBuffer('a_Normal', new Float32Array(positions), 3))
    return -1;


  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);


  var indexBuffer = this.gl.createBuffer();
  if (!indexBuffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  this.gl.bufferData(
      this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
      this.gl.STATIC_DRAW);

  return indices.length;
};

Draw3D.prototype.initArrayBuffer = function(attribute, data, num) {
  // Create a buffer object
  var buffer = this.gl.createBuffer();
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  // Write date into the buffer object
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
  this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = this.gl.getAttribLocation(this.gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
  this.gl.vertexAttribPointer(a_attribute, num, this.gl.FLOAT, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  this.gl.enableVertexAttribArray(a_attribute);

  return true;
};